package com.yueya.auth.utils;

import com.yueya.auth.config.AuthProperties;
import com.yueya.auth.model.Account;
import com.yueya.auth.model.AuthUserDto;
import com.yueya.auth.model.JwtDto;
import com.yueya.auth.realm.Principal;
import com.yueya.common.util.DateUtils;
import io.jsonwebtoken.*;
import org.apache.shiro.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.*;

public class JwtUtil {
    private static Logger logger = LoggerFactory.getLogger(JwtUtil.class);
    /**
     * 解析JWT的Payload
     */
    public static Jws<Claims> parseJwtPayload(String jwsString,String publicKey,String secretType) throws JwtException{
        Assert.hasText(jwsString, "JWT String argument cannot be null or empty.");
        Jws<Claims> jws;
        Key key =null ;
        if (SignatureAlgorithm.HS256.getValue().equals(secretType)){
            key = KeysUtil.getSecretKey(publicKey,getSecretType(secretType));
        } else{
            try {
                key = KeysUtil.getPublicKeyFromString(publicKey,getSecretType(secretType));
            } catch (InvalidKeySpecException|NoSuchAlgorithmException e) {
                logger.error("jwt key err",e);
            }
        }
        jws = Jwts
                .parser()
                // 时钟误差允许2分钟
                .setAllowedClockSkewSeconds(2  *  60)
                .setSigningKey(key)
                .parseClaimsJws(jwsString);
        return jws;
    }

    private static SignatureAlgorithm getSecretType(String type) {
        SignatureAlgorithm signatureAlgorithm;
        switch (type){
            case "HS256": signatureAlgorithm = SignatureAlgorithm.HS256;
            break;
            case "RS256": signatureAlgorithm = SignatureAlgorithm.RS256;
            break;
            default:signatureAlgorithm = SignatureAlgorithm.HS256;
        }
        return signatureAlgorithm;
    }
    public static String signJwt(JwtDto payLoad, Date iat, Date nbf, Date exp, String privateKey,String secretType) {
        String jwt = null;
        try {
            Key key ;
            if (SignatureAlgorithm.HS256.getValue().equals(secretType)){
                key = KeysUtil.getSecretKey(privateKey,getSecretType(secretType));
            } else{
                key = KeysUtil.getPrivateKeyFromString(privateKey,getSecretType(secretType));
            }
            jwt = Jwts.builder()
                    .setSubject(payLoad.getSub())
                    .setIssuedAt(iat)
                    .setIssuer(payLoad.getIss())
                    .setNotBefore(nbf)
                    .setExpiration(exp)
                    .setId(payLoad.getJti())
                    .setAudience(payLoad.getAud())
                    .addClaims(claimToMap(payLoad.getClaims() ))
                    .signWith(key)
                    .compressWith(CompressionCodecs.DEFLATE)
                    .compact();
        } catch (InvalidKeySpecException|NoSuchAlgorithmException e) {
            logger.error("sign key err:",e);
        }
        return jwt;
    }

    public static String createJwtToke(Principal principal, String jwtId, Date date, AuthProperties authProperties) {
        JwtDto jwtDto = new JwtDto();
        jwtDto.setIss(authProperties.getAppId());
        jwtDto.setJti(jwtId);
        /*Map<String,Object> body = new HashMap<>(3);
        body.put("userId",userId);
        body.put("loginName",userName);
        body.put("roles",roles);
        body.put("pers",pres);*/
        Account account  = principal.getAccount();
        AuthUserDto claims = new AuthUserDto();
        claims.setUserId(account.getId());
        claims.setLoginName(account.getAccount());
        claims.setName(account.getName());
        claims.setRoles(principal.getRoles());
        claims.setPres(principal.getPermissions());
        Date nbf = DateUtils.addSeconds(date,-5);
        Date exprieTime = DateUtils.addSeconds(date,authProperties.getExpireTime());
        jwtDto.setClaims(claims);
        return JwtUtil.signJwt(jwtDto, date, nbf , exprieTime,authProperties.getPrivateKey(),authProperties.getSignMode());
    }

    public static String createRefreshToke(Principal principal, String jwtId, Date date, AuthProperties authProperties) {
        JwtDto jwtDto = new JwtDto();
        jwtDto.setIss(authProperties.getAppId());
        jwtDto.setJti(jwtId);
        Date nbf = DateUtils.addSeconds(date,-5);
        Date exprieTime = DateUtils.addHours(date,authProperties.getRefreshTime());
        Account account  = principal.getAccount();
        AuthUserDto claims = new AuthUserDto();
        claims.setUserId(account.getId());
        claims.setLoginName(account.getAccount());
        claims.setName(account.getName());
        jwtDto.setClaims(claims);
        return JwtUtil.signJwt(jwtDto, date, nbf , exprieTime,authProperties.getPrivateKey(),authProperties.getSignMode());
    }

    public static AuthUserDto getClaims(Claims claims) {
        AuthUserDto authUserDto = new AuthUserDto();
        authUserDto.setUserId(claims.get("userId",String.class));
        authUserDto.setLoginName(claims.get("loginName",String.class));
        authUserDto.setName(claims.get("name",String.class));
        authUserDto.setPhoto(claims.get("photo",String.class));
        return authUserDto;
    }

    public  static Map<String,Object> claimToMap(AuthUserDto claims) {
        Map<String,Object> map = new HashMap<>(6);
        map.put("userId",claims.getUserId());
        map.put("loginName",claims.getLoginName());
        map.put("name",claims.getName());
        map.put("photo",claims.getPhoto());
        map.put("roles",claims.getRoles());
        map.put("pres",claims.getPres());
        return map;
    }

}
